package com.xybean.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.transaction.annotation.Transactional;

import com.xybean.dao.page.Page;
import com.xybean.dao.page.PageResult;

@Transactional
public class DAOimpl<E> extends HibernateDaoSupport implements DAO<E> {

	public static Log log = LogFactory.getLog(DAOimpl.class);

	private Class<E> entityClass;

	public DAOimpl() {
		this.entityClass = (Class<E>) ((ParameterizedType) getClass().getGenericSuperclass())
				.getActualTypeArguments()[0];
		log.info("创建默认的dao");
	}

	public DAOimpl(Class<E> entityClass) {
		log.info("创建" + entityClass.getName() + "实体的dao");
		this.entityClass = entityClass;
	}

	public E save(E e) {
		Session s = this.getSession();
		s.save(e);
		return e;
	}

	public void update(E e) {
		Session s = this.getSession();
		s.update(e);
	}

	public E delete(E e) {
		Session s = this.getSession();
		s.delete(e);
		return e;
	}

	public E deletebyId(Integer id) {
		E e = get(id);
		delete(e);
		return e;
	}

	public Criteria createCriteria(Class<?> entityClass, Criterion... criterion) {
		Criteria criteria = this.getSession().createCriteria(entityClass);
		criteria.setCacheable(true);
		for (Criterion c : criterion) {
			criteria.add(c);
		}
		return criteria;
	}

	public Criteria createCriteria(Criterion... criterion) {
		return createCriteria(this.entityClass, criterion);
	}

	public Criteria createCriteria() {
		return createCriteria(this.entityClass);
	}

	public E get(Serializable id) {
		return (E) getHibernateTemplate().get(entityClass, id);
	}

	public List<E> getAll() {
		HibernateTemplate template = getHibernateTemplate();
		template.setCacheQueries(true);
		return template.loadAll(entityClass);
	}

	public Integer getTotalRecords(String hql, Object... values) {
		Finder find = Finder.create(hql);
		Query query = this.getSession().createQuery(find.getRowCountHql());
		query.setCacheable(true);
		for (int i = 0; i < values.length; i++) {
			query.setParameter(i, values[i]);
		}
		return ((Long) query.uniqueResult()).intValue();
	}

	public Integer getTotalRecords(Class<E> entityClass, Criterion... criterion) {
		Criteria criteria = getSession().createCriteria(entityClass);
		criteria.setCacheable(true);
		for (Criterion c : criterion) {
			criteria.add(c);
		}
		return ((Long) criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
	}

	public Integer getTotalRecords(Criterion... criterion) {
		return getTotalRecords(this.entityClass, criterion);
	}

	public List<E> getList(String hql, Object... values) {
		return getList(hql, Integer.MAX_VALUE, values);
	}

	public List<E> getList(String hql, int limit, Object... values) {
		Session s = getSession();
		Query query = s.createQuery(hql);
		query.setCacheable(true);
		for (int i = 0; i < values.length; i++) {
			query.setParameter(i, values[i]);
		}
		query.setMaxResults(limit);
		return query.list();
	}

	public List getComplexList(String hql, int limit, Object... values) {
		Session s = getSession();
		Query query = s.createQuery(hql);
		query.setCacheable(true);
		for (int i = 0; i < values.length; i++) {
			query.setParameter(i, values[i]);
		}
		query.setMaxResults(limit);
		return query.list();
	}

	public List<E> getList(int limit, Criterion... criterions) {
		return getList(limit, "", "", criterions);
	}

	public List<E> getList(Criterion... criterions) {
		return getList(0, criterions);
	}

	public List<E> getList(String ordertype, String property, Criterion... criterions) {
		return getList(0, ordertype, property, criterions);
	}

	public List<E> getList(int limit, String ordertype, String property, Criterion... criterions) {
		String[] p = null;
		String[] o = null;
		if (ordertype != null && !ordertype.equals("") && property != null && !property.equals("")) {
			o = new String[] { ordertype };
			p = new String[] { property };
		}
		return getList(limit, o, p, criterions);
	}

	public List<E> getList(int limit, String[] ordertype, String[] property, Criterion... criterions) {
		Criteria criteria = this.createCriteria();
		criteria.setCacheable(true);
		for (Criterion c : criterions)
			criteria.add(c);
		// 排序
		if (ordertype != null && ordertype.length > 0 && property != null && property.length > 0
				&& ordertype.length == property.length) {
			int i = 0;
			for (String type : ordertype) {
				if (type.toLowerCase().equals("desc")) {
					criteria.addOrder(Order.desc(property[i]));
					i++;
				} else if (type.toLowerCase().equals("asc")) {
					criteria.addOrder(Order.asc(property[i]));
					i++;
				} else {
					return null;
				}
			}
		}
		if (limit != 0)
			criteria.setMaxResults(limit);
		return criteria.list();
	}

	public <T> T findUniqByCriteria(Class<T> entityClass, Criterion... criterion) {
		Criteria criteria = this.getSession().createCriteria(entityClass);
		criteria.setCacheable(true);
		for (Criterion c : criterion) {
			criteria.add(c);
		}
		return (T) criteria.uniqueResult();
	}

	public E findUniqByCriteria(Criterion... criterion) {
		return findUniqByCriteria(this.entityClass, criterion);
	}

	public E findUniqByHql(String hql, Object... values) {
		Query query = this.createQuery(hql, values);
		query.setCacheable(true);
		return (E) query.uniqueResult();
	}

	public PageResult<E> getPageResult(Class<E> entityClass, int currentPage, int pageSize,
			Criterion... criterion) {
		Criteria criteria = this.getSession().createCriteria(entityClass);
		criteria.setCacheable(true);
		for (Criterion c : criterion) {
			criteria.add(c);
		}
		criteria.setFirstResult((currentPage - 1) * pageSize);
		criteria.setMaxResults(pageSize);
		List<E> data = criteria.list();
		Integer totalSize = this.getTotalRecords(entityClass, criterion);
		return createPageResult(currentPage, totalSize, pageSize, data);
	}

	public PageResult<E> getPageResult(int currentPage, int pageSize, Criterion... criterion) {
		return getPageResult(entityClass, currentPage, pageSize, criterion);
	}

	public PageResult<E> getPageResultByLike(String name, String value, int currentPage, int pageSize) {
		String[] names = { name };
		String[] values = { value };
		return getPageResultByLike(names, values, currentPage, pageSize);
	}

	public PageResult<E> getPageResultByLike(String[] names, String[] values, int currentPage, int pageSize) {
		Criteria criteria = this.getSession().createCriteria(entityClass);
		criteria.setCacheable(true);
		for (int i = 0; i < names.length; i++) {
			criteria.add(Restrictions.like(names[i], values[i], MatchMode.ANYWHERE));
		}
		List<E> data = criteria.list();
		criteria = this.getSession().createCriteria(entityClass);
		criteria.setCacheable(true);
		for (int i = 0; i < names.length; i++) {
			criteria.add(Restrictions.like(names[i], values[i], MatchMode.ANYWHERE));
		}
		criteria.setProjection(Projections.rowCount());
		Integer totalSize = (Integer) criteria.uniqueResult();
		return createPageResult(currentPage, totalSize, pageSize, data);
	}

	public PageResult<E> getPageResult(String hql, int currentPage, int pageSize, Object... values) {
		Query query = this.createQuery(hql, values);
		query.setFirstResult(pageSize * (currentPage - 1));
		query.setMaxResults(pageSize);
		query.setCacheable(true);
		for (int i = 0; i < values.length; i++) {
			query.setParameter(i, values[i]);
		}
		List<E> data = query.list();
		Integer totalSize = this.getTotalRecords(hql, values);
		return createPageResult(currentPage, totalSize, pageSize, data);
	}

	private PageResult<E> createPageResult(int currentPage, Integer totalSize, int pageSize, List<E> data) {
		PageResult<E> pageResult = new PageResult<E>();
		Page page = new Page(currentPage, totalSize, pageSize);
		pageResult.setResult(data);
		pageResult.setPage(page);
		return pageResult;
	}

	public Query createQuery(String hql, Object... values) {
		Query query = this.getSession().createQuery(hql);
		query.setCacheable(true);
		for (int i = 0; i < values.length; i++) {
			query.setParameter(i, values[i]);
		}
		return query;
	}

	public SQLQuery createSQLQuery(String sql, Object... values) {
		SQLQuery slqQuery = this.getSession(true).createSQLQuery(sql);
		for (int i = 0; i < values.length; i++) {
			slqQuery.setParameter(i, values[i]);
		}
		return slqQuery;
	}

	public Class<E> getEntityClass() {
		return entityClass;
	}

	public void setEntityClass(Class<E> entityClass) {
		this.entityClass = entityClass;
	}

	public void flush() {
		this.getSession().flush();
		this.getSession().clear();
	}

}
